/**
 * 四、手写实现MyPromise源码
 * 要求：尽可能还原Promise中的每一个API，并通过注释的方式描述思路和原理
 * 
 */



 /**
  * 1.Promise 就是一个类 执行这个类的时候，传递一个执行器，执行器会立即执行
  * 2.Promise 中有三种状态 fulfilled-成功 rejected-败  pending-等待
  * 一旦状态确定就不可更改
  * 3.resolve和reject函数是用来更改状态的
  * resolve: pending => fulfilled
  * reject: pending => rejected
  * 4.then方法内部做的事情是按照状态做事情，如果是成功，则调用成功回调函数，如果失败，则调用失败的回调函数
  * 5.then成功回调有一个参数，表示成功之后的值，失败回调有一个参数，表示失败之后的原因
  * (a)resolve方法异步执行，需要then方法pedding状态下的存储回调函数
  * (b)then方法被执行多次，回调函数需要用数组存储
  * (c)then方法链式调式，则then方法需要返回一个promise对象
  * (c-1)then返回一个值
  * (c-2)then返回一下promise对象
  * (d)then方法可选参数
  * 6.try catch
  * 7.Promise.all
  * 8.Promise.resolve
  * 9.finally
  * 10.catch
  */
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise{
  // 执行器
  constructor(executor) {
    try {
      executor(this.resolve, this.reject)
    } catch (error) {
      this.reject(error)
    }
  }
  // 记录当前状态
  status = PENDING
  // 记录成功后的值
  value = undefined
  // 记录失败后的原因
  reason = undefined
  //=== 异步回调 存储成功回调
  successCallback = []
  //=== 异步回调 存储失败回调
  failCallback = []
  // 成功回调函数-修改状态为fulfilled
  resolve = (value) => {
    // 状态一旦修改，就不能再改变
    if(this.status !== PENDING) retrun
    this.status = FULFILLED
    this.value = value
    //=== 异步回调-判断成功回调是否存在，存在就调用
    while(this.successCallback.length)this.successCallback.shift()()
  }
  // 失败回调函数-修改状态为rejected
  reject = (reason) => {
    // 状态一旦修改，就不能再改变
    if(this.status !== PENDING) retrun
    this.status = REJECTED
    this.reason = reason
    //=== 异步回调-判断失败回调是否存在，存在就调用
    while(this.failCallback.length)this.failCallback.shift()()
  }
  then(successCallback, failCallback) {
    // 兼容then可选参数 then().then().then(value=>{xxx})
    successCallback = successCallback ? successCallback : value => value
    failCallback = failCallback ? failCallback : reason => {throw reason}
    let promise2 = new MyPromise((resolve, reject)=> {
      if (this.status === FULFILLED) {
        // setTimeout创建一个任务，目的先让promise2存在，才能再调用resolvePromise
        setTimeout(()=> {
          try {
            let x = successCallback(this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        }, 0)
        
      } else if(this.status === REJECTED) {
        setTimeout(()=> {
          try {
            let x = failCallback(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        }, 0)
      } else {
        // === 异步回调-异步调用的时候，先存储起来回调方法
        this.successCallback.push(()=>{
          setTimeout(()=> {
            try {
              let x = successCallback(this.value)
              resolvePromise(promise2, x, resolve, reject)
            } catch (error) {
              reject(error)
            }
          }, 0)
        })
        this.failCallback.push(()=>{
          setTimeout(()=> {
            try {
              let x = failCallback(this.reason)
              resolvePromise(promise2, x, resolve, reject)
            } catch (error) {
              reject(error)
            }
          }, 0)
        })
      }
    })

    return promise2
  }

  finally(callback) {
    // 返回一个promise, then兼容异步
    // finally无论成功或失败，都调用，所以在then方法里面的成功或失败状态里面调用
    return this.then(value=>{
     return MyPromise.resolve(callback()).then(()=>value)
    }, reason=>{
      return MyPromise.resolve(callback()).then(()=> {throw reason})
    })
  }

  catch(failCallback) {
    return this.then(undefined, failCallback)
  }

  static all(array) {
    let result = []
    let index = 0
    return new Promise((resolve, reject) => {
      function addData(key, value) {
        result[key] = value
        index++
        if(index === array.length) {
          resolve(result)
        }
      }
      for (let i=0; i < array.length; i++) {
        let current = array[i]
        if(current instanceof MyPromise) {
          // promise对象，用then来接受异步的回调值
          current.then(value=> addData(i, value), reason=> reject(reason))
        } else {
          // 普通对象，有现成的值
          addData(i, current)
        }
      }
    })
  }

  static resolve(value) {
    if(value instanceof MyPromise) return value
    return new MyPromise(resolve=> resolve(value))
  }
}

// 判断x的值是普通值还是promise对象
// 如果是普通值 直接resolve
// 如果是promise对象 查看promise对象的返回结果
// 再根据promise对象的返回结果决定调用resolve还是reject

/**
promise2 === x

let promise = new MyPromise ((resolve, reject) =>{
  resolve('成功')
})

let p1 = promise.then(value => {
  console.log(value)
  return p1
})

p1.then(value => {
  console.log(value)
}, reason=> {
  console.log(reason.message);
})
 */
function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  if(x instanceof MyPromise) {
    x.then(resolve, reject)
  } else {
    resolve(x)
  }
}


